package mvc.struts.annotation.action;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import mvc.struts.annotation.entity.Data;
import mvc.struts.annotation.entity.Handler;
import mvc.struts.annotation.entity.Param;
import mvc.struts.annotation.entity.View;
import mvc.struts.annotation.helper.BeanHelper;
import mvc.struts.annotation.helper.ClassHelper;
import mvc.struts.annotation.helper.ConfigHelper;
import mvc.struts.annotation.helper.ControllerHelper;
import mvc.struts.annotation.util.ArrayUtil;
import mvc.struts.annotation.util.ClassUtil;
import mvc.struts.annotation.util.CodecUtil;
import mvc.struts.annotation.util.JsonUtil;
import mvc.struts.annotation.util.ReflectionUtil;
import mvc.struts.annotation.util.StreamUtil;
import mvc.struts.annotation.util.StringUtil;

import org.apache.commons.lang3.StringUtils;

public class DispatcherServlet extends HttpServlet {

	/**
	 * 
	 */
	@Override
	public void init(ServletConfig servletConfig) throws ServletException {
		
		System.out.println("DispatcherServlet.init() called");
		
		//	Load Class
		Class<?>[] classList = {
				ClassHelper.class,
				BeanHelper.class,
				ControllerHelper.class
		};
		
		for	(Class<?> cls : classList) {
			ClassUtil.loadClass(cls.getName(), true);
		}
		
		//	
	}
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
		IOException, ServletException 
	{	
		//
		String	requestMethod = request.getMethod().toLowerCase();
		
		String servletPath = request.getServletPath();
        String pathInfo = StringUtil.defaultIfEmpty(request.getPathInfo(), "");
        
		String	requestPath   = servletPath + pathInfo;
		
		System.out.println("DispatcherServlet - requestMethod : " + requestMethod);
		System.out.println("DispatcherServlet - requestPath : " + requestPath);
		
        if 	(requestPath.equals("/")) {
            
            try {
                response.sendRedirect(request.getContextPath() + "/index.jsp");
            } catch (Exception e) {
                
                throw new RuntimeException(e);
            }
            
            return;
        }
        
		//
		Handler	handler = ControllerHelper.getHandler(requestMethod, requestPath);
		
		if	(handler != null)
		{
			Class<?>	controllerClass = handler.getControllerClass();
			Object		controllerBean  = BeanHelper.getBean(controllerClass);
			
			Map<String, Object>	paramMap = new HashMap<String, Object>();
			
			Enumeration<String>	paramNames = request.getParameterNames();
			
			while	(paramNames.hasMoreElements())
			{
				String	paramName = paramNames.nextElement();
				String	paramValue = request.getParameter(paramName);
				
				paramMap.put(paramName, paramValue);							
			}
			
			String	body = CodecUtil.decodeURL(StreamUtil.getString(request.getInputStream()));
			
			if	(StringUtil.isNotEmpty(body))
			{
				String[]	params = StringUtils.split(body, "&");
				
				if	(ArrayUtil.isNotEmpty(params))
				{
					for	(String param : params)
					{
						String[]	array = StringUtils.split(param, "=");
					
						if	(ArrayUtil.isNotEmpty(array) && array.length == 2)
						{
							String 	paramName = array[0];
							String	paramValue= array[1];
							
							paramMap.put(paramName, paramValue);
						}
					}
				}
			}
			
			Param	param = new Param(paramMap);
			
			//	Call Action Method
			Method	actionMethod = handler.getActionMethod();
			Object	result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, (Object)param);
			
			//	simple web page name
			if	(result instanceof String)
			{
				System.out.println("After invokeMethod, return result is String");
				
				String	path = (String)result;
				
				if	(StringUtil.isNotEmpty(path))
				{
					if	(path.startsWith("/")) {
						
						response.sendRedirect(request.getContextPath());
					}
					else
					{						
						request.getRequestDispatcher(ConfigHelper.getJspBasePath() + path + ".jsp").forward(request, response);
					}
				}
			}
			else if	(result instanceof View)
			{
				//	Return View
				System.out.println("After invokeMethod, return result is View");
				
				View	view = (View)result;
				String	path = view.getPath();
				
				System.out.println("Return View, path = " + path);
				
				if	(StringUtil.isNotEmpty(path))
				{
					if	(path.startsWith("/")) {
						
						response.sendRedirect(request.getContextPath());
					}
					else
					{
						Map<String, Object>	model = view.getModel();
						
						for	(Map.Entry<String, Object>entry : model.entrySet())
						{
							request.setAttribute(entry.getKey(), entry.getValue());
						}
												
						System.out.println("getRequestDispatcher, path=" + ConfigHelper.getJspBasePath() + path + ".jsp");
						
						request.getRequestDispatcher(ConfigHelper.getJspBasePath() + path + ".jsp").forward(request, response);
					}
				}
			}
			else if (result instanceof Data)
			{
				//	Return Json data
				System.out.println("After invokeMethod, return result is Data");
				
				Data	data = (Data)result;
				
				Object	model = data.getModel();
				
				if	(model != null)
				{
					response.setContentType("application/json");
					response.setCharacterEncoding("UTF-8");
					
					PrintWriter	writer = response.getWriter();
					
					String	json = JsonUtil.toJson(model);
					
					writer.write(json);
					writer.flush();
					writer.close();
				}
			}
		}
		else
		{
			System.out.println("can not find handler for " + requestPath);
		}
	}
	
	/**
	 * 
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws
		IOException, ServletException 
	{	
		doGet(request, response);
	}
	
	/**
	 * 
	 */
//	@Override
//	public void service(HttpServletRequest request, HttpServletResponse response) 
//				throws ServletException, IOException
//	{
//		
//	}
}
